package xin.xihc.lottery.service;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import xin.xihc.jba.sql.Select;
import xin.xihc.jba.sql.clause.Eq;
import xin.xihc.jba.sql.clause.LkLt;
import xin.xihc.jba.sql.clause.LkRt;
import xin.xihc.lottery.LotteryType;
import xin.xihc.lottery.dao.LotteryNumbersDao;
import xin.xihc.lottery.dto.LotteryGroupDTO;
import xin.xihc.lottery.dto.LotteryGroupVO;
import xin.xihc.lottery.model.LotteryNumbers;
import xin.xihc.lottery.param.DetailParam;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Lottery Service
 *
 * @author Leo.Xi
 * @date 2022/2/27
 * @since 1.0
 **/
@Service
@EnableScheduling
public class LotteryService {

    private Map<LotteryType, List<String>> numsCacheMap = new HashMap<>(16);

    @Autowired
    private LotteryNumbersDao lotteryNumbersDao;

    @Scheduled(cron = "0 20 22 * * *")
    public void scheduleCheck() throws IOException {
        LotteryType type = LotteryType.SSQ;
        String url = "https://www.cjcp.com.cn/zoushitu/cjwssq/hqzonghe.html";
        Document document = Jsoup.connect(url)
                                 .get();
        Elements children = document.selectFirst("#tabledata #pagedata")
                                    .children();
        addOrUpdateLottery(type, children);

        // 大乐透
        type     = LotteryType.DLT;
        url      = "https://www.cjcp.com.cn/zoushitu/cjwdlt/qqzonghe.html";
        document = Jsoup.connect(url)
                        .get();
        children = document.selectFirst("#tabledata #pagedata")
                           .children();
        addOrUpdateLottery(type, children);


        ///  更新缓存
        cacheNums();
    }

    protected void cacheNums() {
        for (LotteryType value : LotteryType.values()) {
            Select select = Select.from(LotteryNumbers.class)
                                  .select("nums");
            select.where()
                  .and(new Eq("type", value));
            List<String> nums = lotteryNumbersDao.queryList(select, String.class, null);
            this.numsCacheMap.put(value, nums);
        }
    }

    /**
     * 数据库是否存在
     *
     * @param type
     * @param numsFormat 01,02,03,04,05|06,07
     * @return
     * @author Leo.Xi
     * @date 2022/2/28
     * @since 0.0.1
     */
    public boolean dbExists(LotteryType type, String numsFormat) {
        if (this.numsCacheMap.size() != LotteryType.values().length) {
            cacheNums();
        }
        List<String> numsCache = this.numsCacheMap.get(type);
        if (null != numsCache) {
            return numsCache.contains(numsFormat);
        }
        return false;
    }

    public void addOrUpdateLottery(LotteryType type, Elements children) {
        for (Element child : children) {
            //            String param.getIndex() = child.child(0).text();
            String qishu = child.child(1)
                                .text();
            String nums = child.child(2)
                               .text();
            System.err.printf("%s-%s%n", qishu, nums);
            //
            LotteryNumbers lottery = new LotteryNumbers();
            lottery.setType(type);
            lottery.setQs(qishu);
            boolean exists = lotteryNumbersDao.exists(lottery);
            if (!exists) {
                lottery.setNums(nums);
                String[] ints = nums.split("[,|]");
                lottery.setN1(Integer.valueOf(ints[0]));
                lottery.setN2(Integer.valueOf(ints[1]));
                lottery.setN3(Integer.valueOf(ints[2]));
                lottery.setN4(Integer.valueOf(ints[3]));
                lottery.setN5(Integer.valueOf(ints[4]));
                lottery.setN6(Integer.valueOf(ints[5]));
                lottery.setN7(Integer.valueOf(ints[6]));
                lotteryNumbersDao.add(lottery);
            }
        }
    }

    public int getMax(LotteryType type, int index) {
        int max = 0;
        switch (type) {
            case SSQ:
                if (index > 6) {
                    max = 16;
                } else {
                    max = 33 - 6 + index;
                }
                break;
            case DLT:
                if (index > 5) {
                    max = 12 - 2 + (index - 5);
                } else {
                    max = 35 - 5 + index;
                }
                break;
        }
        return max;
    }

    public List<LotteryGroupVO> numStat(DetailParam param, boolean full) {
        Select select = Select.from(LotteryNumbers.class)
                              .select("n" + param.getIndex() + " as num", "count(*) as count")
                              .groupBy("n" + param.getIndex());
        select.where()
              .and(new Eq("type", param.getType()));
        if (param.getYear() != null) {
            select.where()
                  .and(new LkRt("qs", (param.getYear() + "").substring(2)));
        }
        if (param.getQs() != null) {
            select.where()
                  .and(new LkLt("qs", String.format("%03d", param.getQs())));
        }
        int max = getMax(param.getType(), param.getIndex());

        List<LotteryGroupDTO> list = lotteryNumbersDao.queryList(select, LotteryGroupDTO.class, null);
        int total = list.stream()
                        .mapToInt(LotteryGroupDTO::getCount)
                        .sum();
        Map<Integer, LotteryGroupDTO> numMap = list.stream()
                                                   .collect(Collectors.toMap(LotteryGroupDTO::getNum, x -> x));
        List<LotteryGroupVO> res = new ArrayList<>(max);
        for (int i = 0; i < max; i++) {
            LotteryGroupVO vo = new LotteryGroupVO();
            vo.setNum(i + 1);
            vo.setTotal(total);
            if (numMap.containsKey(vo.getNum())) {
                vo.setCount(numMap.get(vo.getNum())
                                  .getCount());
                vo.setRate(new BigDecimal(vo.getCount()).multiply(new BigDecimal(100))
                                                        .divide(new BigDecimal(total), 2, RoundingMode.HALF_UP));
            }
            if (full || numMap.containsKey(vo.getNum())) {
                res.add(vo);
            }
        }
        return res;
    }

    public int nextNum(LotteryType type, int index, int last) {
        int v = 0;
        do {
            v = new Random().nextInt(getMax(type, index) + 1);
            if (v > last) {break;}
        } while (true);
        return v;
    }


}